/**********************************************************************************************************************
 *
 * Project:    mGameEngine
 * File:       math.h
 * Author:     karooolek
 * Created on: 2009-02-27
 *
 **********************************************************************************************************************/

#ifndef MATH_H_
#define MATH_H_

#define _MATH_DEFINES_DEFINED
#include <cmath>
#include <cstdlib>

namespace mGameEngine
{
// std imports
using std::abs;
using std::acos;
using std::asin;
using std::atan;
using std::atan2;
using std::ceil;
using std::cos;
using std::cosh;
using std::exp;
using std::fabs;
using std::floor;
using std::fmod;
using std::frexp;
using std::ldexp;
using std::log;
using std::log10;
using std::modf;
using std::pow;
using std::sin;
using std::sinh;
using std::sqrt;
using std::tan;
using std::tanh;

#ifdef min
#   undef min
#endif

#ifdef max
#   undef max
#endif

// useful math constants redefined as floats not double
#define M_E        2.718281828f
#define M_LOG2E    1.442695040f
#define M_LOG10E   0.434294481f
#define M_LN2      0.693147180f
#define M_LN10     2.302585092f
#define M_PI       3.141592653f
#define M_PI_2     1.570796326f
#define M_PI_3     1.047197551f
#define M_PI_4     0.785398163f
#define M_1_PI     0.318309886f
#define M_2_PI     0.636619772f
#define M_2_SQRTPI 1.128379167f
#define M_SQRT2    1.414213562f
#define M_1_SQRT2  0.707106781f
#define M_SQRT3    1.732050807f
#define M_1_SQRT3  0.577350269f

/**
 * Calculate base-2 logarithm.
 * @param a argument.
 * @return logarithm value.
 */
template<typename T>
T log2(T a)
{
    return log10(a) * 3.3219280948f;
}

/**
 * Calculate squared value.
 * @param a value
 * @return squared value
 */
template<typename T>
T sqr(T a)
{
    return a *= a;
}

/**
 * Bias value towards another value.
 * @param a value
 * @param b target value
 * @param t bias step
 * @return value a biased towards value b.
 */
template<typename T>
T bias(const T &a, const T &b, const T &t)
{
    return abs(b-a) > t ? a+t*sign(b-a) : b;
}

/**
 * Linear interpolation between two values.
 * @param a first value
 * @param b second value
 * @param t interpolation factor
 * @return linearly interpolated value between a and b
 */
template<typename T>
T lerp(const T &a, const T &b, float t)
{
    return a + t * (b - a);
}

/**
 * Bilinear interpolation between four values.
 * Firstly pairs a/b and c/d are linearly interpolated using lerp1.
 * Then values are interpolated using lerp2.
 * @param a first value
 * @param b second value
 * @param c third value
 * @param d fourth value
 * @param t first interpolation factor
 * @param q second interpolation factor
 * @return bilinearly interpolated value between a, b, c and d.
 */
template<typename T>
T bilerp(const T &a, const T &b,
        const T &c, const T &d,
        float t, float q)
{
    return lerp(lerp(a, b, t), lerp(c, d, t), q);
}

/**
 * Get smaller value.
 * @param a first value
 * @param b second value
 * @return smaller value
 */
template<typename T>
const T &min(const T &a, const T &b)
{
    return a < b ? a : b;
}

/**
 * Get sign of value.
 * @param a value
 * @return 1 if a greater or equal 0, -1 otherwise
 */
template<typename T>
T sign(const T &a)
{
    return a >= T(0) ? T(1) : T(-1);
}

/**
 * Get larger value.
 * @param a first value
 * @param b second value
 * @return larger value
 */
template<typename T>
const T &max(const T &a, const T &b)
{
    return a > b ? a : b;
}

/**
 * Clamp value to range.
 * @param val value to clamp
 * @param min lower range bound
 * @param max upper range bound
 * @return
 */
template<typename T>
T clamp(const T &val, const T &min, const T &max)
{
    return (val < min) ? min : ((val > max) ? max : val);
}

/**
 * Convert radians to degrees.
 * @param rad angle in radians
 * @return angle in degrees
 */
template<typename T>
T deg(T rad)
{
    return rad * 57.2957795131f;
}

/**
 * Convert degrees to radians.
 * @param deg angle in degrees.
 * @return angle in radians
 */
template<typename T>
T rad(T deg)
{
    return deg * 0.01745329252f;
}

/**
 * Generate random number from range.
 * @param max maximum value
 * @param min minimum value
 * @return random number between min and max.
 */
template<typename T>
T random(const T &max = 1, const T &min = 0)
{
    float a = float(std::rand()) / float(RAND_MAX);
    return lerp(min, max, a);
}

}

#endif // MATH_H_
